home *** CD-ROM | disk | FTP | other *** search
/ SGI Developer Toolbox 6.1 / SGI Developer Toolbox 6.1 - Disc 4.iso / public / plan / src / usermenu.c < prev    next >
C/C++ Source or Header  |  1994-08-01  |  18KB  |  621 lines

  1. /*
  2.  * Create and destroy user menus and all widgets in them. All widgets
  3.  * are labels or pushbuttons; they are faster than text buttons. Whenever
  4.  * the user presses a button with text in it, it is overlaid with a Text
  5.  * button. For editing and input into the Text button, see useredit.c.
  6.  *
  7.  *    destroy_user_popup()        remove user popup
  8.  *    create_user_popup()        create user popup
  9.  *    force_user_list_update()    make update_user_lists() unconditional
  10.  *    update_user_lists()        for each user in the list, re-read the
  11.  *                    user's public appts into user[].list
  12.  *                    if necessary
  13.  */
  14.  
  15. #include <stdio.h>
  16. #include <time.h>
  17. #include <sys/types.h>
  18. #include <sys/stat.h>
  19. #ifndef MIPS
  20. #include <stdlib.h>
  21. #endif
  22. #include <pwd.h>
  23. #include <Xm/Xm.h>
  24. #include <Xm/Form.h>
  25. #include <Xm/LabelP.h>
  26. #include <Xm/LabelG.h>
  27. #include <Xm/PushBP.h>
  28. #include <Xm/PushBG.h>
  29. #include <Xm/ToggleB.h>
  30. #include <Xm/ScrolledW.h>
  31. #include <Xm/Text.h>
  32. #include <Xm/Protocols.h>
  33. #include "cal.h"
  34.  
  35. #define NCOLUMNS    4        /* # of widget columns in user list */
  36.  
  37. extern char *mystrdup();
  38. extern void help_callback();
  39. static void create_user_rows(), edit_user_button(), got_user_text(),
  40.         draw_row(), delete_callback(), sort_callback(),
  41.         done_callback(), list_callback(), got_text();
  42. #ifdef MIPS
  43. extern struct passwd *getpwnam();
  44. #endif
  45.  
  46. extern Display        *display;    /* everybody uses the same server */
  47. extern GC        gc;        /* everybody uses this context */
  48. extern Pixel        color[NCOLS];    /* colors: COL_* */
  49. extern Pixmap        pixmap[NPICS];    /* common symbols */
  50. extern struct config    config;        /* global configuration data */
  51. extern struct list    *mainlist;    /* list of all schedule entries */
  52. extern struct mainmenu    mainmenu;    /* all important main window widgets */
  53. extern struct user    *user;        /* user list (from file_r.c) */
  54. extern int        nusers;        /* number of users in user list */
  55.  
  56. static BOOL        have_shell;    /* message popup exists if TRUE */
  57. static Widget        shell;        /* popup menu shell */
  58. static Widget        delete;        /* delete button, for desensitizing */
  59. static Widget        info;        /* info line for error messages */
  60. static Widget        textwidget;    /* if editing, text widget; else 0 */
  61. static int        have_nrows;    /* # of table widget rows allocated */
  62. static int        xedit, yedit;    /* if editing, column/row */
  63. static int        ycurr;        /* current row, 0=none, 1=1st user...*/
  64. static Widget        ulist;        /* user list RowColumn widget */
  65. static Widget        (*utable)[4];    /* all widgets in user list table */
  66.                     /* [0][*] is title row */
  67.  
  68.  
  69.  
  70. /*
  71.  * destroy the popup. Remove it from the screen, and destroy its widgets.
  72.  * Redraw the week menu if there is one.
  73.  */
  74.  
  75. destroy_user_popup()
  76. {
  77.     if (have_shell) {
  78.         XtPopdown(shell);
  79.         XTDESTROYWIDGET(shell);
  80.         have_shell = FALSE;
  81.         mainlist->modified = TRUE;
  82.         draw_week_calendar();
  83.     }
  84. }
  85.  
  86.  
  87. /*
  88.  * create a user popup as a separate application shell.
  89.  */
  90.  
  91. create_user_popup()
  92. {
  93.     Widget            form, scroll, w;
  94.     Arg            args[15];
  95.     int            n;
  96.     Atom            closewindow;
  97.  
  98.     if (have_shell)
  99.         return;
  100.     n = 0;
  101.     XtSetArg(args[n], XmNdeleteResponse,    XmUNMAP);        n++;
  102.     XtSetArg(args[n], XmNiconic,        False);            n++;
  103.     shell = XtAppCreateShell("User List", "plan",
  104.             applicationShellWidgetClass, display, args, n);
  105. #    ifdef EDITRES
  106.     XtAddEventHandler(shell, (EventMask)0, TRUE, 
  107.              _XEditResCheckMessages, NULL);
  108. #    endif
  109.     set_icon(shell, 1);
  110.     form = XtCreateWidget("userform", xmFormWidgetClass,
  111.             shell, NULL, 0);
  112.     XtAddCallback(form, XmNhelpCallback, help_callback, (XtPointer)"user");
  113.  
  114.                             /*-- buttons --*/
  115.     n = 0;
  116.     XtSetArg(args[n], XmNbottomAttachment,    XmATTACH_FORM);        n++;
  117.     XtSetArg(args[n], XmNbottomOffset,    8);            n++;
  118.     XtSetArg(args[n], XmNleftAttachment,    XmATTACH_FORM);        n++;
  119.     XtSetArg(args[n], XmNleftOffset,    8);            n++;
  120.     XtSetArg(args[n], XmNwidth,        80);            n++;
  121.     XtSetArg(args[n], XmNsensitive,        False);            n++;
  122.     delete = w = XtCreateManagedWidget("Delete", xmPushButtonWidgetClass,
  123.             form, args, n);
  124.     XtAddCallback(w, XmNactivateCallback, delete_callback, (XtPointer)0);
  125.     XtAddCallback(w, XmNhelpCallback,     help_callback,   (XtPointer)
  126.                                 "user_delete");
  127.  
  128.     n = 0;
  129.     XtSetArg(args[n], XmNbottomAttachment,    XmATTACH_FORM);        n++;
  130.     XtSetArg(args[n], XmNbottomOffset,    8);            n++;
  131.     XtSetArg(args[n], XmNleftAttachment,    XmATTACH_WIDGET);    n++;
  132.     XtSetArg(args[n], XmNleftWidget,    w);            n++;
  133.     XtSetArg(args[n], XmNleftOffset,    8);            n++;
  134.     XtSetArg(args[n], XmNwidth,        80);            n++;
  135.     w = XtCreateManagedWidget("Sort", xmPushButtonWidgetClass,
  136.             form, args, n);
  137.     XtAddCallback(w, XmNactivateCallback, sort_callback, (XtPointer)0);
  138.     XtAddCallback(w, XmNhelpCallback,     help_callback, (XtPointer)
  139.                                 "user_sort");
  140.  
  141.     n = 0;
  142.     XtSetArg(args[n], XmNbottomAttachment,    XmATTACH_FORM);        n++;
  143.     XtSetArg(args[n], XmNbottomOffset,    8);            n++;
  144.     XtSetArg(args[n], XmNrightAttachment,    XmATTACH_FORM);        n++;
  145.     XtSetArg(args[n], XmNrightOffset,    8);            n++;
  146.     XtSetArg(args[n], XmNwidth,        80);            n++;
  147.     w = XtCreateManagedWidget("Done", xmPushButtonWidgetClass,
  148.             form, args, n);
  149.     XtAddCallback(w, XmNactivateCallback, done_callback, (XtPointer)0);
  150.     XtAddCallback(w, XmNhelpCallback,     help_callback, (XtPointer)
  151.                                 "user_done");
  152.  
  153.     n = 0;
  154.     XtSetArg(args[n], XmNbottomAttachment,    XmATTACH_FORM);        n++;
  155.     XtSetArg(args[n], XmNbottomOffset,    8);            n++;
  156.     XtSetArg(args[n], XmNrightAttachment,    XmATTACH_WIDGET);    n++;
  157.     XtSetArg(args[n], XmNrightWidget,    w);            n++;
  158.     XtSetArg(args[n], XmNrightOffset,    8);            n++;
  159.     XtSetArg(args[n], XmNwidth,        80);            n++;
  160.     w = XtCreateManagedWidget("Help", xmPushButtonWidgetClass,
  161.             form, args, n);
  162.     XtAddCallback(w, XmNactivateCallback, help_callback, (XtPointer)
  163.                                 "user");
  164.     XtAddCallback(w, XmNhelpCallback,     help_callback, (XtPointer)
  165.                                 "user");
  166.  
  167.                             /*-- infotext -- */
  168.     n = 0;
  169.     XtSetArg(args[n], XmNleftAttachment,    XmATTACH_FORM);        n++;
  170.     XtSetArg(args[n], XmNleftOffset,    8);            n++;
  171.     XtSetArg(args[n], XmNrightAttachment,    XmATTACH_FORM);        n++;
  172.     XtSetArg(args[n], XmNrightOffset,    8);            n++;
  173.     XtSetArg(args[n], XmNbottomAttachment,    XmATTACH_WIDGET);    n++;
  174.     XtSetArg(args[n], XmNbottomWidget,    w);            n++;
  175.     XtSetArg(args[n], XmNbottomOffset,    8);            n++;
  176.     XtSetArg(args[n], XmNalignment,         XmALIGNMENT_BEGINNING);    n++;
  177.     info = XtCreateManagedWidget(" ", xmLabelGadgetClass,
  178.             form, args, n);
  179.  
  180.                             /*-- scroll --*/
  181.     n = 0;
  182.     XtSetArg(args[n], XmNtopAttachment,    XmATTACH_FORM);        n++;
  183.     XtSetArg(args[n], XmNtopOffset,        8);            n++;
  184.     XtSetArg(args[n], XmNbottomAttachment,    XmATTACH_WIDGET);    n++;
  185.     XtSetArg(args[n], XmNbottomWidget,    info);            n++;
  186.     XtSetArg(args[n], XmNbottomOffset,    8);            n++;
  187.     XtSetArg(args[n], XmNleftAttachment,    XmATTACH_FORM);        n++;
  188.     XtSetArg(args[n], XmNleftOffset,    8);            n++;
  189.     XtSetArg(args[n], XmNrightAttachment,    XmATTACH_FORM);        n++;
  190.     XtSetArg(args[n], XmNrightOffset,    8);            n++;
  191.     XtSetArg(args[n], XmNwidth,        580);            n++;
  192.     XtSetArg(args[n], XmNheight,        300);            n++;
  193.     XtSetArg(args[n], XmNscrollingPolicy,    XmAUTOMATIC);        n++;
  194.     scroll = XtCreateWidget("uscroll", xmScrolledWindowWidgetClass,
  195.             form, args, n);
  196.     XtAddCallback(scroll, XmNhelpCallback, help_callback, (XtPointer)
  197.                                 "user");
  198.  
  199.     n = 0;
  200.     ulist = XtCreateManagedWidget("ulist", xmBulletinBoardWidgetClass,
  201.             scroll, args, n);
  202.  
  203.     create_user_rows();    /* have_shell must be FALSE here */
  204.  
  205.     XtManageChild(scroll);
  206.     XtManageChild(form);
  207.     XtPopup(shell, XtGrabNone);
  208.  
  209.     closewindow = XmInternAtom(display, "WM_DELETE_WINDOW", False);
  210.     XmAddWMProtocolCallback(shell, closewindow,
  211.                     done_callback, (XtPointer)shell);
  212.     have_shell = TRUE;
  213. }
  214.  
  215.  
  216. /*
  217.  * makes sure there are enough widget rows for schedule entries. Also makes
  218.  * sure that there aren't too many, for speed reasons. Allocate one extra
  219.  * widget row for the title at the top. All the text buttons are
  220.  * label widgets. For performance reasons, they are overlaid by a text
  221.  * widget when pressed.
  222.  * No text is printed into the buttons yet, this is done later by draw_users().
  223.  */
  224.  
  225. static short cell_x    [NCOLUMNS] = {  4, 54, 114, 234 };
  226. static short cell_xs   [NCOLUMNS] = { 30, 60, 120, 300 };
  227. static char *cell_name [NCOLUMNS] = { " ", "Group", "User", "Home" };
  228. static char *cell_help [NCOLUMNS] = { "user_enable", "user_color",
  229.                     "user_name", "user_home" };
  230.  
  231. static void create_user_rows()
  232. {
  233.     int            nrows = nusers+5 - nusers%5;
  234.     int            x, y;
  235.     Arg            args[15];
  236.     int            n;
  237.     int            align;
  238.     char            *name;
  239.     WidgetClass        class;
  240.  
  241.     if (!have_shell)                /* check # of rows: */
  242.         have_nrows = 0;
  243.     if (nrows <= have_nrows)
  244.         return;
  245.  
  246.     n = (nrows+1) * NCOLUMNS * sizeof(Widget *);
  247.     if (utable && !(utable = (Widget (*)[])realloc(utable, n)) ||
  248.        !utable && !(utable = (Widget (*)[])malloc(n)))
  249.         fatal("no memory");
  250.  
  251.     for (x=0; x < NCOLUMNS; x++) {
  252.         for (y=have_nrows; y <= nrows; y++) {
  253.         align = XmALIGNMENT_BEGINNING;
  254.         XtUnmanageChild(ulist);
  255.         name  = cell_name[x];
  256.         class = xmPushButtonWidgetClass;
  257.         n = 0;
  258.         if (y) {
  259.             if (x == 0) {
  260.                 class = xmToggleButtonWidgetClass;
  261.                 XtSetArg(args[n], XmNselectColor,
  262.                         color[COL_TOGGLE]);    n++;
  263.             }
  264.             name  = " ";
  265.         } else {
  266.             class = xmLabelWidgetClass;
  267.             align = XmALIGNMENT_CENTER;
  268.         }
  269.         XtSetArg(args[n], XmNx,            cell_x[x]);    n++;
  270.         XtSetArg(args[n], XmNy,            10 + 30*y);    n++;
  271.         XtSetArg(args[n], XmNwidth,        cell_xs[x]);    n++;
  272.         XtSetArg(args[n], XmNheight,        30);        n++;
  273.         XtSetArg(args[n], XmNalignment,         align);        n++;
  274.         XtSetArg(args[n], XmNrecomputeSize,    False);        n++;
  275.         XtSetArg(args[n], XmNtraversalOn,    True);        n++;
  276.         XtSetArg(args[n], XmNhighlightThickness,0);        n++;
  277.         XtSetArg(args[n], XmNshadowThickness,    x && y);    n++;
  278.         utable[y][x] = XtCreateManagedWidget(name, class,
  279.                 ulist, args, n);
  280.         if (y)
  281.             XtAddCallback(utable[y][x],
  282.                 x ? XmNactivateCallback
  283.                   : XmNvalueChangedCallback,
  284.                 list_callback, (XtPointer)(x + y * NCOLUMNS));
  285.         XtAddCallback(utable[y][x], XmNhelpCallback, help_callback,
  286.                         (XtPointer)cell_help[x]);
  287.         }
  288.     }
  289.     for (y=have_nrows; y <= nrows; y++)
  290.         draw_row(y);
  291.     have_nrows = nrows;
  292.  
  293.     XtManageChild(ulist);
  294. }
  295.  
  296.  
  297. /*-------------------------------------------------- editing ----------------*/
  298. /*
  299.  * turn a text label into a Text button, to allow user input. This is done
  300.  * by simply installing a text widget on top of the label widget. The proper
  301.  * user name or home dir is put into the text widget. The previously edited
  302.  * button is un-edited.
  303.  */
  304.  
  305. static void edit_user_button(doedit, x, y)
  306.     BOOL            doedit;        /* TRUE=edit, FALSE=unedit */
  307.     int            x;        /* column, 0..NCOLUMNS-1* */
  308.     int            y;        /* row, y=0: title */
  309. {
  310.     Arg            args[15];
  311.     int            n;
  312.     char            *text;
  313.  
  314.     if (textwidget) {
  315.         char *string = XmTextGetString(textwidget);
  316.         got_user_text(xedit, yedit, string);
  317.         XtFree(string);
  318.         XtDestroyWidget(textwidget);
  319.         if (yedit && yedit <= nusers)
  320.             user[yedit-1].suspended = 0;
  321.         draw_row(yedit);
  322.         create_user_rows();
  323.     }
  324.     textwidget = 0;
  325.     if (!doedit)
  326.         return;
  327.  
  328.     if (y > nusers+1)
  329.         y = nusers+1;
  330.     n = 0;
  331.     XtSetArg(args[n], XmNx,            cell_x[x]);        n++;
  332.     XtSetArg(args[n], XmNy,            10 + 30*y);        n++;
  333.     XtSetArg(args[n], XmNwidth,        cell_xs[x]);        n++;
  334.     XtSetArg(args[n], XmNheight,        30);            n++;
  335.     XtSetArg(args[n], XmNrecomputeSize,    False);            n++;
  336.     XtSetArg(args[n], XmNpendingDelete,    True);            n++;
  337.     XtSetArg(args[n], XmNhighlightThickness,0);            n++;
  338.     XtSetArg(args[n], XmNshadowThickness,    1);            n++;
  339.     XtSetArg(args[n], XmNbackground,    color[COL_TEXTBACK]);    n++;
  340.     textwidget = XtCreateManagedWidget("text", xmTextWidgetClass,
  341.             ulist, args, n);
  342.     XtAddCallback(textwidget, XmNactivateCallback, got_text, (XtPointer)0);
  343.     XtAddCallback(textwidget, XmNhelpCallback, help_callback,
  344.                         (XtPointer)cell_help[x]);
  345.     XmProcessTraversal(textwidget, XmTRAVERSE_CURRENT);
  346.  
  347.     text = y > nusers ? "" : x == 2 ? user[y-1].name : user[y-1].home;
  348.     print_text_button(textwidget, "%s", text);
  349.     xedit = x;
  350.     yedit = y;
  351. }
  352.  
  353. static void got_user_text(x, y, string)
  354.     int            x;        /* column, 0..NCOLUMNS-1* */
  355.     int            y;        /* row, y=0: title */
  356.     char            *string;    /* text entered by user */
  357. {
  358.     struct passwd        *pw;        /* for searching home dirs */
  359.     register struct user    *u;
  360.     char            buf[100];
  361.     int            i;
  362.  
  363.     if (!y--)
  364.         return;
  365.     while ((i = strlen(string)) && string[i-1] == ' ')
  366.         string[i-1] = 0;
  367.     if (!*string)
  368.         return;
  369.     if (y >= nusers) {
  370.         int n = ++nusers * sizeof(struct user);
  371.         if (user && !(user = (struct user *)realloc(user, n)) ||
  372.            !user && !(user = (struct user *)malloc(n)))
  373.             fatal("no memory");
  374.         y = nusers-1;
  375.         u = user+y;
  376.         u->name         = 0;
  377.         u->home         = 0;
  378.         u->suspended = 0;
  379.         u->color     = 0;
  380.         u->time         = 0;
  381.         u->list         = 0;
  382.     }
  383.     u = user+y;
  384.     if (x == 2) {                    /* name */
  385.         if (u->name)
  386.             free(u->name);
  387.         if (u->home)
  388.             free(u->home);
  389.         u->name = mystrdup(string);
  390.         u->home = 0;
  391.     } else {                    /* home */
  392.         if (u->home)
  393.             free(u->home);
  394.         u->home = mystrdup(string);
  395.         if (*u->home && access(u->home, 1))
  396.             print_button(info, "%s: cannot access", u->home);
  397.     }
  398.     if (!*string)
  399.         print_button(info, "Null user not allowed");
  400.  
  401.     if (u->name && *u->name && !u->home) {        /* default home? */
  402.         if (pw = getpwnam(string))
  403.             u->home = mystrdup(pw->pw_dir);
  404.         else {
  405.             sprintf(buf, "~%s", string);
  406.             print_button(info, "%s: no such user", string);
  407.             u->home = mystrdup(buf);
  408.         }
  409.     }
  410. }
  411.  
  412.  
  413. /*
  414.  * draw all buttons of row y. y must be > 0 because 0 is the title row.
  415.  * If y is > nusers, the row is blanked.
  416.  */
  417.  
  418. static void draw_row(y)
  419.     int            y;
  420. {
  421.     Arg            arg;
  422.     register struct user    *u = &user[y-1];
  423.  
  424.     if (y < 1)
  425.         return;
  426.     if (y <= nusers) {                    /* valid row */
  427.         XtSetArg(arg, XmNset, !u->suspended);
  428.         XtSetValues (utable[y][0], &arg, 1);
  429.         XtSetArg(arg, XmNbackground, color[COL_WUSER_0 + u->color]);
  430.         XtSetValues (utable[y][1], &arg, 1);
  431.         print_button(utable[y][2], u->name);
  432.         print_button(utable[y][3], u->home);
  433.     } else {                        /* blank row */
  434.         XtSetArg(arg, XmNset, 0);
  435.         XtSetValues (utable[y][0], &arg, 1);
  436.         XtSetArg(arg, XmNbackground, color[COL_BACK]);
  437.         XtSetValues (utable[y][1], &arg, 1);
  438.         print_button(utable[y][2], " ");
  439.         print_button(utable[y][3], " ");
  440.     }
  441. }
  442.  
  443.  
  444.  
  445. /*-------------------------------------------------- read lists -------------*/
  446. /*
  447.  * make sure that all user lists are up-to-date. Don't check more than once
  448.  * every 10 seconds though, to prevent lots of lengthy stat() calls.
  449.  */
  450.  
  451. static time_t    last_test;    /* last time we stat'ed all files */
  452.  
  453. force_user_list_update()
  454. {
  455.     last_test = 0;
  456. }
  457.  
  458. update_user_lists()
  459. {
  460.     BOOL            reread;        /* need to re-stat files? */
  461.     register struct user    *u;        /* current user slot */
  462.     int            n;        /* # of current user slot */
  463.     struct stat        file;        /* for reading file mod time */
  464.     char            path[1024];    /* user's .dayplan file name */
  465.  
  466.     reread = get_time() > last_test + 10;
  467.     for (u=user, n=0; n < nusers; n++, u++) {
  468.         if (u->suspended || !reread && u->list || !u->home)
  469.             continue;
  470.         sprintf(path, "%s/%s", u->home, DB_FILE);
  471.         if (u->list) {
  472.             if (stat(path, &file)) {
  473.                 perror(path);
  474.                 continue;
  475.             }
  476.             if (last_test && u->time < file.st_mtime)
  477.                 continue;
  478.             free((char *)u->list);
  479.             u->list = 0;
  480.         }
  481.         if (!readfile(&u->list, path, FALSE))
  482.             perror(path);
  483.     }
  484.     last_test = get_time();
  485. }
  486.  
  487.  
  488. /*-------------------------------------------------- callbacks --------------*/
  489. /*
  490.  * Delete, Add-all, Sort, and Done buttons
  491.  * All of these routines are direct X callbacks.
  492.  */
  493.  
  494. /*ARGSUSED*/
  495. static void delete_callback(widget, item, data)
  496.     Widget                widget;
  497.     int                item;
  498.     XmToggleButtonCallbackStruct    *data;
  499. {
  500.     int                n;
  501.     Arg                args;
  502.  
  503.     if (ycurr && ycurr <= nusers) {
  504.         edit_user_button(FALSE, 0, 0);
  505.         for (n=ycurr-1; n < nusers; n++)
  506.             user[n] = user[n+1];
  507.         n = --nusers * sizeof(struct user);
  508.         if (n && !(user = (struct user *)realloc(user, n)))
  509.             fatal("no memory");
  510.         for (n=1; n <= have_nrows; n++)
  511.             draw_row(n);
  512.     }
  513.     if (!ycurr) {
  514.         XtSetArg(args, XmNsensitive, 0);
  515.         XtSetValues(delete, &args, 1);
  516.     }
  517. }
  518.  
  519.  
  520. /* aren't prototypes annoying? :-) */
  521. #if defined(ULTRIX) || defined(MIPS)
  522.         /* this means Ultrix 4.2A. If Ultrix 4.3 complains about */
  523.         /* a missing const, change the following definition. */
  524. #define CONST
  525. #else
  526. #define CONST const
  527. #endif
  528.  
  529. static int compare(u, v) register CONST void *u, *v; {
  530.     return(  ((struct user *)u)->color == ((struct user *)v)->color
  531.     ? strcmp(((struct user *)u)->name,    ((struct user *)v)->name)
  532.     :        ((struct user *)u)->color -  ((struct user *)v)->color); }
  533.  
  534. /*ARGSUSED*/
  535. static void sort_callback(widget, item, data)
  536.     Widget                widget;
  537.     int                item;
  538.     XmToggleButtonCallbackStruct    *data;
  539. {
  540.     Arg                args;
  541.     int                n;
  542.  
  543.     if (nusers) {
  544.         edit_user_button(FALSE, 0, 0);
  545.         XtSetArg(args, XmNsensitive, 0);
  546.         XtSetValues(delete, &args, 1);
  547.         qsort(user, nusers, sizeof(struct user), compare);
  548.         for (n=1; n <= nusers; n++)
  549.             draw_row(n);
  550.     }
  551. }
  552.  
  553. /*ARGSUSED*/
  554. static void done_callback(widget, item, data)
  555.     Widget                widget;
  556.     int                item;
  557.     XmToggleButtonCallbackStruct    *data;
  558. {
  559.     edit_user_button(FALSE, 0, 0);
  560.     destroy_user_popup();
  561. }
  562.  
  563.  
  564. /*
  565.  * one of the buttons in the list was pressed
  566.  */
  567.  
  568. /*ARGSUSED*/
  569. static void list_callback(widget, item, data)
  570.     Widget                widget;
  571.     int                item;
  572.     XmToggleButtonCallbackStruct    *data;
  573. {
  574.     int                x = item % NCOLUMNS;
  575.     int                y = item / NCOLUMNS;
  576.     Arg                arg;
  577.  
  578.     if (y > nusers) {                    /* new entry */
  579.         if (x != 2) {
  580.             print_button(info, "Enter user name first");
  581.             return;
  582.         }
  583.         ycurr = 0;
  584.         edit_user_button(TRUE, x, nusers+1);
  585.     } else {                        /* old entry */
  586.         ycurr = y;
  587.         switch(x) {
  588.           case 0:                    /* on/off */
  589.             user[y-1].suspended = !data->set;
  590.             break;
  591.           case 1:                    /* color */
  592.             user[y-1].color++;
  593.             user[y-1].color &= 7;
  594.             XtSetArg(arg, XmNbackground,
  595.                 color[COL_WUSER_0 + user[y-1].color]);
  596.             XtSetValues(widget, &arg, 1);
  597.             break;
  598.           case 2:                    /* username */
  599.           case 3:                    /* home dir */
  600.             edit_user_button(TRUE, x, y);
  601.         }
  602.     }
  603.     XtSetArg(arg, XmNsensitive, ycurr > 0);
  604.     XtSetValues(delete, &arg, 1);
  605.     print_button(info, " ");
  606. }
  607.  
  608.  
  609. /*
  610.  * the user pressed Return in a text entry button
  611.  */
  612.  
  613. /*ARGSUSED*/
  614. static void got_text(widget, item, data)
  615.     Widget                widget;
  616.     int                item;
  617.     XmToggleButtonCallbackStruct    *data;
  618. {
  619.     edit_user_button(FALSE, 0, 0);
  620. }
  621.